#include "exFatFs.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"

//这个文件是对文件系统操作的一些封装和补充
//主要维护了一个链表存储指定路径app_path下所有bin文件,
//proc函数，可以更新一次app_path路径下的文件到链表中，
//并返回最近一次添加的(若添加多个，返回排序最后的那个)bin文件路径到proc_app_path指针中

#define EXFATFS_LOG(fmt, ...)  printf("[exFATFS.c|%d] "fmt"\r\n",__LINE__,##__VA_ARGS__)

typedef struct _exFileInfo *pexFileInfo; 
typedef struct _exFileInfo
{
    FILINFO     fno;   //文件信息
    pexFileInfo next;  //指向下一个文件信息
    uint8_t is_remain; //这个文件还保留着 
} exFileInfo; 
pexFileInfo header = NULL; //链表头指针

// 比较两个FILINFO结构是否相同(相同返回1，不同返回0)
// Sheep
static uint8_t exFatFs_Finfo_Is_Same(const FILINFO *f1, const FILINFO *f2) 
{
    if (f1->fsize != f2->fsize) return 0;// 比较文件大小
    if (f1->fdate != f2->fdate) return 0;// 比较最后修改日期
    if (f1->ftime != f2->ftime) return 0;// 比较最后修改时间
    if (f1->fattrib != f2->fattrib) return 0; // 比较文件属性
    if (strcmp(f1->fname, f2->fname) != 0) return 0;// 比较长文件名
    return 1; // 所有字段都相同
}
//将新扫描的斌文件插入到链表中
//Sheep
static uint8_t exFatFs_Add(pexFileInfo pexfno)
{
    uint8_t res = 0; //默认文件不存在，加入成功
    if(header == NULL) 
    {
        pexFileInfo temp = (pexFileInfo)malloc(sizeof(exFileInfo));
        if(temp==NULL) res = 2; //内存不够
        else 
        {
            *temp = *pexfno; //将输入的结构体赋值给temp
            header = temp;  //链表为空,插入的那个就是头指针
        }
    }
    else
    {
        pexFileInfo p = header; //从头指针开始
        while(1)
        {
            if(exFatFs_Finfo_Is_Same(&(p->fno), &(pexfno->fno))) //如果两个文件的fno相同
            {
                res = 1;//文件已经存在,加入失败
                p->is_remain = 1; //标志这个文件还在
            }
            if(p->next == NULL)break; //已经找到到链表尾了
            else p = p->next;//往后挪一个
        }
        if(res == 0)//文件不存在的话，将他加在链表末尾
        {
            pexFileInfo temp = (pexFileInfo)malloc(sizeof(exFileInfo));
            if(temp==NULL) res = 2; //内存不够
            else 
            {
                *temp = *pexfno; //将输入的结构体赋值给temp
                p->next = temp;  //加在链表末尾
            }
        }
    }
    return res;
}

// 删除掉链表中的被标记已删除文件
// Sheep
static uint8_t exFatFs_Del(void)
{
    pexFileInfo p = header;
    pexFileInfo q = p; //暂存p的前一链表节点
    if(header == NULL)return 0; //不需要删除
    else
    {
        do
        {
            if(p->is_remain)
            {
                p->is_remain = 0; //检查过保留，清除位，方便下次检查
                q = p; 
                p = p->next; //q p 均往后移
            }else
            {
                if(p == header)//如果删掉的是头结点的话，头结点也后移
                {
                    header = p->next; 
                    free(p);
                    p = q = header;
                }
                else
                {
                    q->next = p->next; //连接下一个节点
                    free(p);
                    p = q->next; //扔指向q的下一个节点
                }
            }
        } while (p != NULL);
    }
    return 0;
}

//*****************************外部函数*************************** */
/**
* @brief : FRESULT exFatFs_Proc(const TCHAR * app_path, FRESULT (*builtin)(void) ,FRESULT (*destruct)(void), char* proc_app_path)
* @param : app_path : 指定路径，如"0:/"，
* @param : builtin : 文件系统挂载函数，方便外界使用不同的储存介质
* @param : destruct : 文件系统卸载函数，方便外界使用不同的储存介质
* @param : proc_app_path : 最近一次添加的bin文件路径(绝对路径)，如果最近没有添加bin文件，返回全0数组
* @attention : None
* @author : Sheep
* @date : 24/10/25
*/
FRESULT exFatFs_Proc(const TCHAR * app_path, FRESULT (*builtin)(void) ,FRESULT (*destruct)(void), char* proc_app_path)
{
    DIR dir;
    exFileInfo temp = {.next = NULL};
    FRESULT res = FR_OK;
    uint8_t ret = 1, bin_num = 0; //默认文件没有改动过，bin文件的数量
    if(app_path==NULL || builtin==NULL || destruct==NULL){res = FR_INVALID_NAME; return res;} 
    res = builtin();
    res = f_opendir(&dir,app_path);
    if(res == FR_OK)
    {
        EXFATFS_LOG("open dir OK");
        while(f_readdir(&dir,&(temp.fno)) == FR_OK && temp.fno.fname[0] != 0)
        {
            EXFATFS_LOG("file name = %s",temp.fno.fname);
            if(strcmp( &(temp.fno.fname[strlen(temp.fno.fname)-4]), ".BIN" ) == 0 )
            {
                bin_num++;
                temp.is_remain = 1; //所有新加入的文件都是保留的(避免下面删除的时候给删掉嘞)
                ret = exFatFs_Add(&temp);    
                if(ret == 0)//提取最后一个新加入的文件名
                    memcpy((void*)proc_app_path,temp.fno.fname,strlen(temp.fno.fname)+1);
                else if (ret == 1)
                {
                    if(proc_app_path != NULL)
                        memset(proc_app_path, 0, strlen(proc_app_path)); //如果没有新加入的文件，则清空proc_app_path
                }
                else if(ret == 2)
                {//如果内存申请失败的话
                    EXFATFS_LOG("Heap is not enough");//打印提示
                }
            }
        }
        if(bin_num == 0 && proc_app_path != NULL)//bin文件被删光嘞
            memset(proc_app_path, 0, strlen(proc_app_path)); 
    }else EXFATFS_LOG("APP_DIR_PATH is not a dir");
    exFatFs_Del(); //根据这次检查，从链表中删除掉已删除的bin文件
    res = f_closedir(&dir);
    res = destruct();
    return res;
}
/**
* @brief : void exFatFs_Print(void)
* @attention : 将链表中所有文件信息打印出来，用于调试
* @author : Sheep
* @date : 24/10/25
*/
void exFatFs_Print(void)
{
    pexFileInfo p = header;
    while(p != NULL)
    {
        EXFATFS_LOG("file name = %s",p->fno.fname);
        p = p->next;
    }
}
/**
* @brief : void exFatFs_fdate2yyyymmdd(DWORD fdate, int *year, int *month, int *day) 
* @param : fdate : 文件日期 fno.fdate，year\monnth\day,年月日;
* @author : Sheep
* @date : 24/10/25
*/
void exFatFs_fdate2yyyymmdd(DWORD fdate, int *year, int *month, int *day) 
{
    *year = ((fdate >> 9) & 0x7F) + 1980;
    *month = (fdate >> 5) & 0x0F;
    *day = fdate & 0x1F;
}
/**
* @brief : void exFatFs_ftime2hhmmss(DWORD ftime, int *hour, int *minute, int *second) 
* @param : ftime : 文件时间 fno.ftime，hour\minute\second,时分秒;
* @author : Sheep
* @date : 24/10/25
*/
void exFatFs_ftime2hhmmss(DWORD ftime, int *hour, int *minute, int *second) 
{
    *hour = (ftime >> 11) & 0x1F;
    *minute = (ftime >> 5) & 0x3F;
    *second = (ftime & 0x1F) * 2;
}
